package classification;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import javax.swing.plaf.SliderUI;

import sun.reflect.generics.tree.Tree;

import com.sun.corba.se.impl.encoding.OSFCodeSetRegistry.Entry;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
import com.sun.xml.internal.bind.v2.runtime.reflect.opt.Const;

/**
 *  <p>
 *  功能
 *  </p>
 *  
 *  <p>Copyright  xxxx公司 2012 All right reserved.</p>
 *  @author Administrator 时间 2012-6-22 下午3:56:52
 *  @version 1.0 
 *  </br>
 *  最后修改人 无
 * 
 */
public class Hunt {
	public Hunt(){
		Initialize();
	}
	
	private final double ratio = 0.8;//0.6666;
	private final int threshold = 20;
	private List<List<String>> data = new ArrayList<List<String>>();
	private List<List<String>> trainingData = new ArrayList<List<String>>();
	private List<List<String>> testingData = new ArrayList<List<String>>();
	
	private void Initialize() {
		
		// TODO Auto-generated method stub
		readData();
		randomSubsampling();
		BinaryNode root = new BinaryNode();
		treeGrowth(root, trainingData);
		printTree(root, 0);
		//test();
		System.out.print("\n\n测试数据集");
		testData(testingData, root);
		System.out.print("\n训练数据集");
		testData(trainingData, root);
		
	}

	private void testData(List<List<String>> testingData, BinaryNode root) {
		
		// TODO Auto-generated method stub
		int fault = 0;
		
		for (List<String> l : testingData) {
			compareRule(l, root);
			//System.out.println(l.get(l.size()-1));
			if (!l.get(1).equals(l.get(l.size()-1))) {
				fault++;
			} 
		}
		
		System.out.println("正确率："+(double)100*(testingData.size()-fault)/testingData.size()+"%");
	}

	private void compareRule(List<String> l, BinaryNode node) {
		
		// TODO Auto-generated method stub
		if (node.left == null && node.right == null) {
			l.add(node.label);
			return;
		}
		
		if (Double.parseDouble(l.get(node.tc.num)) <= node.tc.split) {
			compareRule(l, node.left);
		} else {
			compareRule(l, node.right);
		}
		
	}

	private String s = "||||||||||||||||||||||||||||||||||||||||||||";
	
	private void printTree(BinaryNode node, int depth) {
		
		// TODO Auto-generated method stub
		//layer++;
		
		if (node.left == null && node.right == null) {
			System.out.print(node.label);
			return;
		}
		
		System.out.print("\n"+s.substring(0, depth));
		System.out.print("属性"+node.tc.num+" <= "+node.tc.split+":");
		//System.out.print("|");
		//leftLayer++;
		printTree(node.left, depth+1);
		System.out.print("\n"+s.substring(0, depth));
		System.out.print("属性"+node.tc.num+" > "+node.tc.split+":");
		//System.out.print("|");
		//rightLayer++;
		printTree(node.right, depth+1);
	}

	private void treeGrowth(BinaryNode node, List<List<String>> trainingData) {
		
		// TODO Auto-generated method stub
		node.B = 0;
		node.M = 0;
		node.tc = findBestSplit(trainingData);
		
		List<List<String>> leftData = new ArrayList<List<String>>();
		List<List<String>> rightData = new ArrayList<List<String>>();
		for (List<String> list : trainingData) {
			if (Double.parseDouble(list.get(node.tc.num)) <= node.tc.split) {
				leftData.add(list);
			} else {
				rightData.add(list);
			}
			
			if (list.get(1).equals("B")) {
				node.B++;
			} else {
				node.M++;
			}
		}
		
		
		if (node.B == 0 || node.M == 0 || node.B + node.M < threshold) {
			node.tc = null;
			node.left = null;
			node.right = null;
			if (node.B >= node.M) {
				node.label = "B";
			} else {
				node.label = "M";
			}
			return;
		}
		/*
		BinaryNode root = new BinaryNode();
		root.tc = findBestSplit(trainingData);
		*/
		
		node.left = new BinaryNode();
		node.right = new BinaryNode();
		
		treeGrowth(node.left, leftData);
		treeGrowth(node.right, rightData);
		
	}

	private TestCond findBestSplit(List<List<String>> trainingData) {
		
		// TODO Auto-generated method stub
		int count = trainingData.get(0).size();
		List<Double> attribute = new ArrayList<Double>();
		HashMap<Integer, Double[]> hm = new HashMap<>();
		for (int i = 2; i < count; i++) {
			for (List<String> l : trainingData) {
				attribute.add(Double.parseDouble(l.get(i)));
			}
			Collections.sort(attribute);
			
			//List<Double> splitAtrribute = new ArrayList<Double>();
			
			HashMap<Double, Double> GINI = new HashMap<>();
			
			for (int j = 0; j < attribute.size()-1; j++) {
				//splitAtrribute.add((attribute.get(j)+attribute.get(j+1))/2);
				
				double split = (attribute.get(j)+attribute.get(j+1))/2;
				double[] less = {0,0};
				double[] more = {0,0};
				for (List<String> l : trainingData) {
					if(Double.parseDouble(l.get(i))<=split){
						if (l.get(1).equals("B")) {
							less[0]++;
						}
						else {
							less[1]++;
						}
					}
					else {
						if (l.get(1).equals("B")) {
							more[0]++;
						}
						else {
							more[1]++;
						}
					}
				}
				GINI.put(split, Gini(less, more));
			}
			
			Double[] splitAndGINI = {(double) 0,(double) 1};
			
			for (java.util.Map.Entry<Double, Double> entry : GINI.entrySet()) {
				if (splitAndGINI[1]>entry.getValue()) {
					splitAndGINI[0] = entry.getKey();
					splitAndGINI[1] = entry.getValue();
				}
			}
			
			//System.out.println(splitAndGINI[0]+"   "+splitAndGINI[1]+"   split and gini");
			
			hm.put(i, splitAndGINI);
		}
		
		int num = 0;
		Double[] splitAndGINI = {(double) 0, (double) 1};
		
		for (java.util.Map.Entry<Integer, Double[]> entry : hm.entrySet()) {
			if (splitAndGINI[1]>entry.getValue()[1]) {
				num = entry.getKey();
				splitAndGINI = entry.getValue();
			}
		}
		
		System.out.println(num+" split  "+splitAndGINI[0]+" gini "+splitAndGINI[1]);
		
		return new TestCond(num, splitAndGINI[0], splitAndGINI[1]);
		
		//return null;
		
	}

	private double Gini(BinaryNode b) {
		double B = (double)b.B;
		double M = (double)b.M;
		return 1 - (B/(B+M))*(B/(B+M)) - (M/(B+M))*(M/(B+M));
	}
	
	private double Gini(double[] less, double[] more) {
		double gini1 = 1 - (less[0]/(less[0]+less[1]))*(less[0]/(less[0]+less[1])) - (less[1]/(less[0]+less[1]))*(less[1]/(less[0]+less[1]));
		double gini2 = 1 - (more[0]/(more[0]+more[1]))*(more[0]/(more[0]+more[1])) - (more[1]/(more[0]+more[1]))*(more[1]/(more[0]+more[1]));
		double sum = less[0] + less[1] + more[0] + more[1];
		return gini1*(less[0]+less[1])/sum + gini2*(more[0]+more[1])/sum;
	}
	
	private void test() {
		//int i = 0;
		// TODO Auto-generated method stub
		for (List<String> l : data) {
			for (String string : l) {
				System.out.print(string+",");
			}
			System.out.println("");
		}
	}
	
	public void readData() {
		try{
			File wdbc;
			System.out.println(System.getProperty("user.dir"));
			wdbc = new File(System.getProperty("user.dir")+"\\bin\\wdbc.data");
			System.out.println(wdbc.exists());
			FileReader fr = new FileReader(wdbc);
			BufferedReader br = new BufferedReader(fr);
			String str;
			int i = 0;
			while ((str = br.readLine())!=null) {
				data.add(new ArrayList<String>());
				String[] line = str.split(",");
				for (String s : line) {
					//data.add(new ArrayList<>());
					data.get(i).add(s);
				}
				i++;
			}
		}
		catch (IOException e) {
			// TODO: handle exception
		}
	}
	
	private void randomSubsampling() {
		int total = data.size();
		int numberOfTraining = (int) ((double)total*ratio);
		System.out.println(numberOfTraining);
		int[] training = new int[numberOfTraining];
		int intRd = 0;
        int count = 0;
        int flag = 0;
        while(count<numberOfTraining){
            Random rdm = new Random(System.currentTimeMillis());
            intRd = Math.abs(rdm.nextInt())%total;
            for(int i=0;i<count;i++){
                if(training[i]==intRd){
                    flag = 1;
                    break;
                }else{
                    flag = 0;
                }
            }
            if(flag==0){
                training[count] = intRd;
                count++;
            }
        }
        
        //java.util.Collections.sort(training);
        Arrays.sort(training);
        
        /*
        for (int i : training) {
        	System.out.println(i);
		}
        
        /*
        for (int i : training) {
        	System.out.println(i);
			trainingData.add(data.get(i));
		}
        */
        
        int j = 0;
        for (int i = 0; i < total; i++) {
        	//if(j<training.)
			if (i == training[j]) {
				//System.out.println(i);
				trainingData.add(data.get(i));
				j++;
				if (j>=training.length) {
					//System.out.println("aaaaaa");
					j = training.length - 1;
				}
				
			}
			else {
				testingData.add(data.get(i));
			}
		}
	}

}

